/**
 * @author [siwi]
 * @email [siwi@siwi.me]
 * @create date 2017-09-18 02:44:10
 * @modify date 2017-09-18 02:44:10
 * @desc [siwi Oauth ]
*/

const Client = require('../../mongooses/oauth/Client')
const AccessToken = require('../../mongooses/oauth/AccessToken')
const RefreshToken = require('../../mongooses/oauth/RefreshToken')
const Code = require('../../mongooses/oauth/Code')
const User = require('../../mongooses/rbac/User')
const Jwt = require('../../lib/Jwt')
const Params = require('../../lib/params')
const constant = require('../../../config/constant')
class AuthorizeController {
    constructor() {

    }
    /**
     * token
     * 
     * @param {any} ctx 
     * @param {any} next 
     * @memberof OauthController
     */
    async token(ctx, next) {
        /** 检查参数**/
        let require = ['grant_type', 'client_id', 'client_secret']

        let check_require = await Params.check(require, ctx.request.body)
        if (check_require) {
             ctx.throw(500, `${check_require}`)
        }
        let grant_type = ctx.request.body.grant_type //生成token的类型  authorization_code password refresh_token
        let client_id = ctx.request.body.client_id
        let client_secret = ctx.request.body.client_secret

        /** 将授权码转化为访问令牌 grant_type = authorization_code **/
        async function grant_type_authorization_code (ctx) {
            let redirect_uri = ctx.request.body.redirect_uri 
            let oauth_code= ctx.request.body.code
            let require = ['redirect_uri', 'code']
            let check_require = await Params.check(require, ctx.request.body)
            if (check_require) {
                 ctx.throw(500, `${check_require}`)
            }

            let data = await Jwt.verify_code(oauth_code)
            if (!data) {
                ctx.throw(500, '非法code')
            }
            let code_id = data.data.code_id
            let user_id = data.data.user_id
            let client_id = data.data.client_id
            let code = await Code.findById({'_id': code_id})
            /** 验证code **/
            if (code.user_id != user_id || code.client_id != client_id) {
                ctx.throw(500, '非法code')
            }
            if (code.revoked == false) {
                ctx.throw(500, 'code失效')
            }

            let user = await User.findById({'_id': user_id})
            let client = await Client.findById({'_id': client_id})
            /** 生成access_token **/
            let access_token_expires_at = constant.ACCESS_TOKEN_EXPIRE_AT()
            let refresh_token_expires_at = constant.REFRESH_TOKEN_EXPIRE_AT()

            let access_token = await AccessToken.create({
                user_id: user._id,
                client_id: client._id,
                name: client.name,
                scopes: code.scopes,
                revoked: true,
                expires_at: access_token_expires_at * 1000
            })
            if (!access_token) {
                ctx.throw(500, '生成access_token出错')
            }

            let refresh_token = await RefreshToken.create({
                access_token_id: access_token._id,
                revoked: true,
                expires_at: refresh_token_expires_at * 1000
            })
            if (!refresh_token) {
                ctx.throw(500, '生成refresh_token出错')
            }

            /** access_token签名 这里默认都是id **/
            let sign_access_token_data = {
                exp: access_token_expires_at,
                data:{
                    access_token: access_token._id,
                    code: code._id,
                    user: user._id,
                    client: client._id,
                    created_at: Date.now()
                }
            }
            /** refresh_token签名 这里默认都是id **/
            let sign_refresh_token_data = {
                exp: access_token_expires_at,
                data:{
                    access_token: access_token._id,
                    refresh_token: refresh_token._id,
                    created_at: Date.now() 
                }
            }
            let res = {
                access_token: await Jwt.sign(sign_access_token_data),
                refresh_token: await Jwt.sign(sign_refresh_token_data),
                expires_at: access_token_expires_at
            }
            
            await Code.update({'_id': code._id}, {'$set': {'revoked': false}})
            return res
        }
        /** 密码授权令牌 grant_type = password **/
        async function  grant_type_password (ctx) {
            let client_id = ctx.request.body.client_id,
            client_secret = ctx.request.body.client_secret,
            username = ctx.request.body.username,
            password = ctx.request.body.password,
            scope = ctx.request.body.scope
            let require = ['username', 'password']
            let check_require = await Params.check(require, ctx.request.body)
            if (check_require) {
                 ctx.throw(500, `${check_require}`)
            }
            let user = await User.check_password(username, password)
            if (!user) {
                ctx.throw('用户名或密码错误')
            }
    
            let client = await Client.findById(client_id)
    
            if (!client) {
                ctx.throw('客户端不存在')
            }
    
            if (client.secret != client_secret) {
                ctx.throw('秘钥错误')
            }
            if (client.password_client == false) {
                ctx.throw('错误的客户端类型')
            }

            /** 生成access_token **/
            let access_token_expires_at = constant.ACCESS_TOKEN_EXPIRE_AT()
            let refresh_token_expires_at = constant.REFRESH_TOKEN_EXPIRE_AT()

            let access_token = await AccessToken.create({
                user_id: user._id,
                client_id: client._id,
                name: client.name,
                scopes: scope,
                revoked: true,
                expires_at: access_token_expires_at * 1000
            })
            if (!access_token) {
                ctx.throw(500, '生成access_token出错')
            }

            let refresh_token = await RefreshToken.create({
                access_token_id: access_token._id,
                revoked: true,
                expires_at: refresh_token_expires_at * 1000
            })
            if (!refresh_token) {
                ctx.throw(500, '生成refresh_token出错')
            }

            /**  access_token签名 这里默认都是id **/
            let sign_access_token_data = {
                exp: access_token_expires_at,
                data:{
                    access_token: access_token._id,
                    user: user._id,
                    client: client._id,
                    created_at: Date.now()                    
                }
            }
            /** refresh_token签名 这里默认都是id **/
            let sign_refresh_token_data = {
                exp: access_token_expires_at,
                data:{
                    access_token: access_token._id,
                    refresh_token: refresh_token._id,
                    created_at: Date.now()
                }
            }
            let res = {
                access_token: await Jwt.sign(sign_access_token_data),
                refresh_token: await Jwt.sign(sign_refresh_token_data),
                expires_at: access_token_expires_at
            }
            return res
        }

        /** 刷新令牌 grant_type = refresh_token **/
        async function  grant_type_refresh_token (ctx) {
            let oauth_refresh_token = ctx.request.body.refresh_token
            let decode = await Jwt.verify(oauth_refresh_token)
            let scope = ctx.request.body.scope
            if (!decode) {
                ctx.throw('refresh_token 错误')
            }

            let time = Date.now()   // 毫秒
            let exp = decode.exp    // 过期时间 秒
            
            if (time > exp * 1000) {
                ctx.throw('refresh_token 过期')
            }
            
            let data = decode.data
            let access_token_id = data.access_token
            let refresh_token_id = data.refresh_token
            
            let token = await AccessToken.findById({'_id': access_token_id})
            let user_id = token.user_id
            let client_id = token.client_id

            let user = await User.findById({'_id': user_id})
            let client = await Client.findById({'_id': client_id})

            /** 生成access_token **/
            let access_token_expires_at = constant.ACCESS_TOKEN_EXPIRE_AT()
            let refresh_token_expires_at = constant.REFRESH_TOKEN_EXPIRE_AT()

            let access_token = await AccessToken.create({
                user_id: user._id,
                client_id: client._id,
                name: client.name,
                scopes: scope,
                revoked: true,
                expires_at: access_token_expires_at * 1000
            })
            if (!access_token) {
                ctx.throw(500, '生成access_token出错')
            }

            let refresh_token = await RefreshToken.create({
                access_token_id: access_token._id,
                revoked: true,
                expires_at: refresh_token_expires_at * 1000
            })
            if (!refresh_token) {
                ctx.throw(500, '生成refresh_token出错')
            }

            /** access_token签名 这里默认都是id **/
            let sign_access_token_data = {
                exp: access_token_expires_at,
                data:{
                    access_token: access_token._id,
                    user: user._id,
                    client: client._id,
                    created_at: Date.now()
                }
            }
            /** refresh_token签名 这里默认都是id **/
            let sign_refresh_token_data = {
                exp: access_token_expires_at,
                data:{
                    access_token: access_token._id,
                    refresh_token: refresh_token._id,
                    created_at: Date.now()
                }
            }
            let res = {
                access_token: await Jwt.sign(sign_access_token_data),
                refresh_token: await Jwt.sign(sign_refresh_token_data),
                expires_at: access_token_expires_at
            }
            
            /** refresh_token access_token 设置为过期 **/
            await RefreshToken.update({'_id': refresh_token_id}, {'$set': {'revoked': false}})
            await AccessToken.update({'_id': access_token_id}, {'$set': {'revoked': false}})
            return res
        }
        
        // 客户端凭证授权令牌 grant_type = client_credentials
        async function  grant_type_client_credentials (ctx) {
            
        }

        if (grant_type == 'authorization_code') {
            let res = await grant_type_authorization_code(ctx)
            ctx.body = res
            return next()
        }
        if (grant_type == 'password') {
            let res = await grant_type_password(ctx)
            ctx.body = res
            return next()
        }
        // refresh_token 
        if (grant_type == 'refresh_token') {
            let res = await grant_type_refresh_token(ctx)
            ctx.body = res
            return next()
        }
        ctx.throw('500', '未匹配到')
    }
    async authorize (ctx, next) {
        /** 检查参数**/
        let require = ['client_id', 'redirect_uri', 'response_type', 'scope']
        let check_require = await Params.check(require, ctx.request.query)
        if (check_require) {
            ctx.throw(500, `${check_require}`)
        }
        let response_type = ctx.request.query.response_type
        /** response_type = token **/
        async function response_type_token(ctx) {
            let data = {
                client_id:ctx.request.query.client_id,
                redirect_uri:ctx.request.query.redirect_uri,
                response_type:ctx.request.query.response_type,
                scope:ctx.request.query.scope,
            }
        }

        /** response_type = code **/
        async function response_type_code(ctx) {
            let client_id = ctx.request.query.client_id
            let redirect_uri = ctx.request.query.redirect_uri
            let scope = ctx.request.query.scope
    
            let client = await Client.findById({'_id': client_id})

            if (!client) {
                ctx.throw(500, `非法客户端`)
            }
            if (client.redirect != redirect_uri) {
                ctx.throw(500, `非法回调地址`)
            }
            let expires_at = Math.floor(Date.now())+ 3600 * 1000
            // 生成 code
            let code_res = await Code.create({
                name: client.name,
                client_id: client._id,
                user_id: client.user_id,
                revoked: true,
                scopes: scope,
                expires_at: expires_at
            })
            let sign_code = {
                exp: expires_at,    //
                data:{
                    code_id: code_res._id,
                    user_id: client.user_id,
                    client_id: client._id
                }
            }
            let code = await Jwt.sign_code(sign_code)
            return {
                code: code,
                expires_at: expires_at
            }
        }

        if (response_type == 'code') {
            let res = await response_type_code(ctx)
            ctx.body = res
            return next()
        }

        if (response_type == 'token') {
            let res = await response_type_token(ctx)
            ctx.body = res
            return next()
        }

        ctx.body = {
            'err_code': null,
            'err_msg': '非法响应类型'
        }
        
    }
    async code (ctx, next) {
        ctx.body =1
    }
    /**
     * 
     * 获取用户
     * 
     * @param {any} ctx 
     * @param {any} next 
     * @returns 
     * @memberof AuthorizeController
     */
    async user (ctx, next) {
        let oauth = ctx.oauth
        let res = await User.findById({'_id': oauth.user_id})
        ctx.body = res
    }
}
module.exports = new AuthorizeController()